import sys
import os
import time
import re
import json
from queue import Queue

from PyQt5.QtCore import QStringListModel, QUrl, QJsonValue, pyqtSlot
from PyQt5.QtWidgets import QApplication, QMainWindow, QDesktopWidget, QInputDialog, QMessageBox
from PyQt5.QtWebChannel import QWebChannel

from pyqt_note_ui import UiMainWindow
from webdav import JGWebdav
from threading import Thread


class AppMainWindow(QMainWindow, UiMainWindow):
    jg_webdav: JGWebdav

    def __init__(self, parent=None):
        super(AppMainWindow, self).__init__(parent)

        # 初始化jg_webdav
        self.jg_webdav = None
        self.jg_username = ""
        self.jg_folder_name = "pyqt-note"
        self.jg_password = ""
        # 实例化QWebChannel
        self.channel = QWebChannel()
        # 笔记本列表数据model
        self.notebook_list_model = QStringListModel()
        # 笔记本列表数据
        self.notebook_list_data = []
        # 当前选中笔记本索引
        self.current_selected_notebook = None

        # 笔记列表数据model
        self.note_list_model = QStringListModel()
        # 笔记列表数据
        self.note_list_data = []
        # 当前选中笔记索引
        self.current_selected_note = None

        # 基本路径
        self.base_path = os.path.join(
            os.path.expanduser("~"), r".config", "qypt-note")
        # local-data本地数据存放路径
        self.local_data_path = os.path.join(
            os.path.expanduser("~"), r".config", "qypt-note", "local-data")
        # 配置文件路径
        self.config_file_path = os.path.join(
            os.path.expanduser("~"), r".config", "qypt-note", "config.json")

        # 初始化ui
        self.setupUi(self)

        # 设置窗口居中显示
        self.set_window_center()

        # 初始化配置
        self.init_config()

        # 加载Markdown编辑器
        self.load_mdeditor()

        # 绑定事件
        self.bind_event()

        # 获取笔记本列表数据
        self.get_local_notebooks()

        # 初始化jg_webdav
        self.init_webdav()

    # 设置窗口居中
    def set_window_center(self):
        """ 设置窗口居中 """

        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    # 初始化配置
    def init_config(self):
        """ 初始化配置文件 """

        # 注册Bridge与js交互
        self.mdEditorWbrowser.page().setWebChannel(self.channel)
        self.channel.registerObject("pythonBridge", self)

        # 基目录
        if not os.path.exists(self.base_path):
            os.makedirs(self.base_path)

        # 配置文件不存在则创建，文件为空则添加默认内容
        if not os.path.exists(self.config_file_path):
            with open(self.config_file_path, 'a', encoding="utf8") as f:
                f.write("""[{"username": "xxx@qq.com", "foldername": "pyqt-note", "password": "xxx"}]""")
        else:
            with open(self.config_file_path, 'r+', encoding="utf8") as fille:
                content = fille.read()
                if content is None or content == "":
                    fille.write("""[{"username": "xxx@qq.com", "foldername": "pyqt-note", "password": "xxx"}]""")

        # local-data文件夹不存在则创建
        if not os.path.exists(self.local_data_path):
            os.makedirs(self.local_data_path)

    # 初始化jg_webdav
    def init_webdav(self):
        """ 初始化jg_webdav """

        try:
            with open(self.config_file_path, "r") as f:
                cf_dic = json.load(f)
                # print(cf_dic)
                username = cf_dic[0].get("username", "")
                folder = cf_dic[0].get("foldername", "pyqt-note")
                password = cf_dic[0].get("password", "")
                self.jg_webdav = JGWebdav(username, password, folder)
        except:
            self.jg_webdav = JGWebdav(self.jg_username, self.jg_password, self.jg_folder_name)

    # 加载Markdown编辑器
    def load_mdeditor(self):
        """ 加载Markdown编辑器 """

        url = os.path.join(os.getcwd(), "mdeditor", "index.html")
        self.mdEditorWbrowser.load(QUrl.fromLocalFile(url))

    # 绑定各控件事件
    def bind_event(self):
        """ 绑定各控件事件 """

        # 绑定笔记本点击事件
        self.lvNotebookList.clicked.connect(self.selected_notebook)
        # 绑定笔记本双击打开事件
        self.lvNotebookList.doubleClicked.connect(self.open_notebook)
        # 绑定添加笔记本按钮事件
        self.btnAddNotebook.clicked.connect(self.add_notebook)
        # 绑定删除笔记本按钮事件
        self.btnRemoveNotebook.clicked.connect(self.remove_notebook)

        # 绑定笔记本点击事件
        self.lvNoteList.clicked.connect(self.selected_note)
        # 绑定笔记双击打开事件
        self.lvNoteList.doubleClicked.connect(self.open_note)
        # 绑定添加笔记按钮事件
        self.btnAddNote.clicked.connect(self.add_note)
        # 绑定删除笔记按钮事件
        self.btnRemoveNote.clicked.connect(self.remove_note)

        # 设置按钮点击事件
        self.btnSetting.clicked.connect(self.set_appsettings)
        # 同步到云端按钮事件
        self.btnSyncToCloud.clicked.connect(self.sync_to_cloud)
        # 同步到本地按钮事件
        self.btnSyncToLocal.clicked.connect(self.sync_to_local)

    # 笔记本被选中
    def selected_notebook(self, index):
        """ 笔记本被选中 """

        # print("选中的笔记本是：", self.notebook_list_data[index.row()])
        # 当前选中笔记本
        self.current_selected_notebook = self.notebook_list_data[index.row()]

    # 笔记被选中
    def selected_note(self, index):
        """ 笔记被选中 """

        # print("选中的笔记是：", self.note_list_data[index.row()])
        # 当前选中笔记本
        self.current_selected_note = self.note_list_data[index.row()]

    # 打开笔记本
    def open_notebook(self, index):
        """ 打开笔记本 """

        # 当前选中笔记本
        self.current_selected_notebook = self.notebook_list_data[index.row()]
        # 笔记本名称
        notebook_name = self.notebook_list_data[index.row()]
        # 获取笔记本下全部笔记
        self.get_notes(notebook_name)

    # 添加笔记本
    def add_notebook(self):
        """ 添加笔记本 """

        # 弹出添加笔记本输入框
        name, ok = QInputDialog.getText(self, "添加笔记本", "请输入笔记本名称")
        if ok and (name is not None) and (name != ""):
            # 创建文件夹
            os.makedirs(os.path.join(self.local_data_path, name))
            # 更新笔记本列表数据
            self.notebook_list_data.append(name)
            self.notebook_list_model.setStringList(self.notebook_list_data)
            self.lvNotebookList.setModel(self.notebook_list_model)

            t_ccn = Thread(target=self.jg_webdav.create_cloud_notebook, args=(name,))
            t_ccn.start()

    # 删除笔记本
    def remove_notebook(self):
        """ 删除笔记本 """

        name = self.current_selected_notebook
        if name is not None:
            res = QMessageBox.question(self, "提示", "确定要删除笔记本 ‘" + name + "‘ 吗？删除后不可恢复！",
                                       QMessageBox.Yes | QMessageBox.No,
                                       QMessageBox.Yes)
            if res == QMessageBox.Yes:
                path = os.path.join(self.local_data_path, name)
                try:
                    # 删除本地文件
                    os.removedirs(path)

                    # 重新载入笔记本列表数据
                    self.get_local_notebooks()
                    # 置空当前笔记本
                    self.current_selected_notebook = None
                except:
                    QMessageBox.critical(
                        self, "提示", "删除失败", QMessageBox.Yes, QMessageBox.Yes)

                # 删除云端笔记本
                t_dcn = Thread(target=self.jg_webdav.delete_cloud_notebook, args=(name,))
                t_dcn.start()


            else:
                # print("点击了no")
                return
        else:
            QMessageBox.warning(self, "提示", "请选择要删除的笔记本",
                                QMessageBox.Yes, QMessageBox.Yes)

    # 获取本地笔记本列表
    def get_local_notebooks(self):
        """ 获取本地笔记本列表 """

        self.notebook_list_data = []
        for name in os.listdir(self.local_data_path):
            self.notebook_list_data.append(name)

        self.notebook_list_model.setStringList(self.notebook_list_data)
        self.lvNotebookList.setModel(self.notebook_list_model)

    # 获取笔记本下全部笔记
    def get_notes(self, current_notebook):
        """ 获取笔记本下全部笔记 """

        self.note_list_data = []
        for name in os.listdir(os.path.join(self.local_data_path, current_notebook)):
            self.note_list_data.append(name.replace(".md", ""))

        self.note_list_model.setStringList(self.note_list_data)
        self.lvNoteList.setModel(self.note_list_model)

    # 打开笔记
    def open_note(self, index):
        """ 打开笔记 """

        # 笔记本名称
        note_name = self.note_list_data[index.row()]
        note_file = os.path.join(self.local_data_path, self.current_selected_notebook, (note_name + ".md"))

        note_content = ""
        with open(note_file, 'r', encoding="utf8") as f:
            # print(f.read())
            note_content = f.read()

        # 执行js加载笔记内容
        # self.mdEditorWbrowser.page().runJavaScript(
        #     "setTitleAndMarkdown(`{0}`,`{1}`);".format(note_name, note_content))

        data = {"note_name": note_name, "note_content": note_content}
        self.mdEditorWbrowser.page().runJavaScript("setTitleAndMarkdown({0});".format(data))

    # 添加笔记
    def add_note(self):
        """ 添加笔记 """

        notebook = self.current_selected_notebook
        temp_note_file = time.strftime("%Y%m%d%H%M%S", time.localtime())
        temp_note_file_path = os.path.join(self.local_data_path, notebook,
                                           temp_note_file + ".md")
        # print(temp_note_file)
        with open(temp_note_file_path, 'w') as f:
            f.write("")

        self.note_list_data.append(temp_note_file)
        self.note_list_model.setStringList(self.note_list_data)
        self.lvNoteList.setModel(self.note_list_model)

    # 删除笔记
    def remove_note(self):
        """ 删除笔记 """

        notebook = self.current_selected_notebook
        note_name = self.current_selected_note
        if (notebook is not None) and (note_name is not None):
            res = QMessageBox.question(self, "提示", "确定要删除笔记 ‘" + note_name + "‘ 吗？删除后不可恢复！",
                                       QMessageBox.Yes | QMessageBox.No,
                                       QMessageBox.Yes)
            if res == QMessageBox.Yes:
                note_file = os.path.join(
                    self.local_data_path, notebook, (note_name + ".md"))
                try:
                    # 删除本地文件
                    os.remove(note_file)

                    # 重新载入笔记列表数据
                    self.note_list_data.remove(note_name)
                    self.note_list_model.setStringList(self.note_list_data)
                    self.lvNoteList.setModel(self.note_list_model)

                    # 置空当前笔记本
                    self.current_selected_note = None

                    # 执行js清空编辑框内容
                    self.mdEditorWbrowser.page().runJavaScript(
                        "setTitleAndMarkdown('','');")
                except:
                    QMessageBox.critical(
                        self, "提示", "删除失败", QMessageBox.Yes, QMessageBox.Yes)

                # 删除云端笔记
                t_dcnote = Thread(target=self.jg_webdav.delete_cloud_note, args=(notebook, note_name))
                t_dcnote.start()

            else:
                # print("点击了no")
                return
        else:
            QMessageBox.warning(self, "提示", "请选择要删除的笔记本",
                                QMessageBox.Yes, QMessageBox.Yes)

    # 保存笔记
    @pyqtSlot(QJsonValue)
    def save_note(self, note):
        note_title = note["title"].toString()
        note_content = note["content"].toString()
        # print(note_title)
        # print(note_content)
        # 当前笔记本
        notebook = self.current_selected_notebook
        # 当前笔记
        old_note = self.current_selected_note

        if (notebook is not None) and (old_note is not None):
            if note_title is None:
                QMessageBox.information(
                    self, "提示", "标题不能为空", QMessageBox.Yes, QMessageBox.Yes)
                return

            if re.match(r"^\d+$", note_title) is not None:
                QMessageBox.information(
                    self, "提示", "请输入新标题", QMessageBox.Yes, QMessageBox.Yes)
                return

            old_note_file = os.path.join(self.local_data_path, notebook, (old_note + ".md"))
            new_note_file = os.path.join(self.local_data_path, notebook, (note_title + ".md"))
            # 写入内容
            with open(old_note_file, "w", encoding="utf8") as f:
                f.write(note_content)
            # 重命名
            os.rename(old_note_file, new_note_file)
            # 更新笔记列表数据
            self.note_list_data[self.note_list_data.index(old_note)] = note_title
            self.note_list_model.setStringList(self.note_list_data)
            self.lvNoteList.setModel(self.note_list_model)

            # 置空当前笔记本
            self.current_selected_note = None

            QMessageBox.information(
                self, "提示", "保存成功", QMessageBox.Yes, QMessageBox.Yes)

            # 上传笔记到云端
            t_untc = Thread(target=self.jg_webdav.upload_note_to_cloud, args=(new_note_file, notebook, note_title))
            t_untc.start()

        else:
            QMessageBox.information(
                self, "提示", "您似乎还未打开笔记", QMessageBox.Yes, QMessageBox.Yes)
            return

    # 应用设置
    def set_appsettings(self):
        with open(self.config_file_path, "r") as f:
            cf_json_content = json.load(f)
            # print(cf_json_content)

        value, ok = QInputDialog.getMultiLineText(self, "设置", "请填写内容以配置坚果云:", str(cf_json_content).replace("'", '"'))
        if ok:
            cf_dic = json.loads(value)
            # print(cf_dic)
            username = cf_dic[0].get("username", "")
            folder = cf_dic[0].get("foldername", "pyqt-note")
            password = cf_dic[0].get("password", "")
            self.jg_webdav = JGWebdav(username, password, folder)

            try:
                with open(self.config_file_path, "w") as cf_json_file:
                    json.dump(cf_dic, cf_json_file)
            except:
                QMessageBox.warning(self, "提示", "配置失败，请检查", QMessageBox.Yes, QMessageBox.Yes)
                return
        else:
            return

    # 同步到云端
    def sync_to_cloud(self):
        local_notebooks = os.listdir(self.local_data_path)
        cloud_notebooks = self.jg_webdav.cloud_notebooks()
        # print(local_notebooks)
        # print(cloud_notebooks)
        # 计算差集
        dif_set = set(local_notebooks).difference(set(cloud_notebooks))  # 差集
        # print(difset)
        # 同步笔记本文件夹
        for notebooks in dif_set:
            self.jg_webdav.create_cloud_notebook(notebooks)

        # 遍历同步笔记文件
        for notebook in local_notebooks:
            notebook_dir = os.path.join(self.local_data_path, notebook)
            notes = os.listdir(notebook_dir)
            if len(notes) == 0:
                continue
            for note in notes:
                note_file_path = os.path.join(notebook_dir, note.replace(".md", ""))
                self.jg_webdav.upload_note_to_cloud(note_file_path, notebook, note)

        QMessageBox.information(self, "提示", "同步完成", QMessageBox.Yes, QMessageBox.Yes)

    # 同步到本地
    def sync_to_local(self):
        cloud_notebooks = self.jg_webdav.cloud_notebooks()
        local_notebooks = os.listdir(self.local_data_path)

        # 计算差集
        dif_set = set(cloud_notebooks).difference(set(local_notebooks))  # 差集
        # 同步笔记本文件夹
        for notebooks in dif_set:
            os.makedirs(os.path.join(self.local_data_path, notebooks))

        # 遍历同步下载笔记文件到本地
        for notebook in cloud_notebooks:
            notes = self.jg_webdav.cloud_notes(notebook)
            if len(notes) == 0:
                continue
            for note in notes:
                local_note_path = os.path.join(self.local_data_path, note[note.index("/") + 1:].replace("/", os.sep))
                # print(local_note_path)
                self.jg_webdav.download_note_to_local(note, local_note_path)

        QMessageBox.information(self, "提示", "同步完成", QMessageBox.Yes, QMessageBox.Yes)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    # 初始化
    appMainWin = AppMainWindow()
    # 将窗口控件显示在屏幕上
    appMainWin.show()
    # 程序运行，sys.exit方法确保程序完整退出。
    sys.exit(app.exec_())
